﻿
#region Usings

using PolyGameEngine.GamePieces.Common;
using PolyGameEngine.Tools;

using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

#endregion

namespace PolyGameEngine.GamePieces
{
    public abstract class GamePiece : DrawableGameComponent
    {
        public static Vector2 _ScreenOffset = Vector2.Zero;
        public int SaveLineIndex;
        public string GPID;
        #region Fields
        public Sprite sprite;
        public Texture2D drawTexture
        {
            get
            {
                return sprite.Texture;
            }
        }
        public SpriteFont DefaultFont
        {
            get { return physicsGame.Fonts[0]; }
        }
        public Body body;
        public Vector2 originalPosition;
        public PhysicsGame physicsGame;
        public Material material;
        public float drawingDepth;
        public World world;
        public int timeSinceLastCollision = 0;

        #endregion

        #region Services
        public SpriteBatch spriteBatch
        {
            get
            {
                return physicsGame.spriteBatch;
            }
        }
        public AssetCreator assetCreator
        {
            get
            {
                return physicsGame.assetcreator;
            }
        }
        #endregion

        #region Body Properties
        public Vector2 position
        {
            get
            {
                return ConvertToScreen(body.Position);
            }
            set
            {
                body.Position = ConvertToSim(value);
            }
        }
        public Vector2 Velocity
        {
            get
            {
                return ConvertToScreen(body.LinearVelocity);
            }
            set
            {
                body.LinearVelocity = ConvertToSim(value);
            }
        }
        public float AngularVelocity
        {
            get
            {
                return ConvertToScreen(body.AngularVelocity);
            }
            set
            {
                body.AngularVelocity = ConvertToSim(value);
            }
        }

        public float rotation
        {
            get
            {
                return body.Rotation;
            }
            set
            {
                body.Rotation = value;
            }
        }
        public float restitution
        {
            get
            {
                return body.Restitution;
            }
            set
            {
                body.Restitution = value;
            }
        }
        public float mass
        {
            get
            {
                return body.Mass;
            }
            set
            {
                body.Mass = value;
            }
        }
        public BodyType bodyType
        {
            get
            {
                return body.BodyType;
            }
            set
            {
                body.BodyType = value;
            }

        }
        public float friction
        {
            get
            {
                
                return body.Friction;
            }
            set
            {
                try
                {
                    body.Friction = value;
                }
                catch
                {}
            }
        }
        public object userData
        {
            get
            {
                return body.UserData;
            }
            set
            {
                body.UserData = value;
                foreach (Fixture fix in body.FixtureList)
                    fix.UserData = value;
            }
        }
        public Fixture defaultFixture
        {
            get
            {
                return body.FixtureList[0];
            }
        }
        public Shape defaultShape
        {
            get
            {
                return defaultFixture.Shape;
            }
        }
        public bool ColidedWithThisGP = false;
        public Fixture ColidingFixture;
        #endregion

        #region Constructors, Initialization and Draw
        public float r; // rotation radians variable
        public GamePiece(PhysicsGame PhysicsGame, World World, Material Material, Vector2 Position, float Rotation, float DrawingDepth, int saveLineIndex, string GPID)
            : base(PhysicsGame)
        {
            SaveLineIndex = saveLineIndex;
            this.GPID = GPID;
            this.Enabled = false;
            physicsGame = PhysicsGame;
            world = World;
            drawingDepth = DrawingDepth;
            body = new Body(World);
            r = MathHelper.ToRadians(Rotation);
            material = Material;
            originalPosition = Position;
            position = Position;
            this.Enabled = true;
        }

        public override void Initialize()
        {
            userData = this;
            ChangeMaterial(material);
            body.IgnoreCCD = true;
            rotation = r;
            base.Initialize();
        }
        public override void Update(GameTime gameTime)
        {
            timeSinceLastCollision += gameTime.ElapsedGameTime.Milliseconds;
            if (ColidingFixture != null)
            {
                if (ColidedWithThisGP&&Vector2.Distance(ColidingFixture.Body.Position, body.Position)<=ConvertToSim(55))
                    ((Unit)(ColidingFixture.UserData)).canJump = true;
                if (Vector2.Distance(ColidingFixture.Body.Position, body.Position) >= 1)
                {
                    ((Unit)(ColidingFixture.UserData)).canJump = false; 
                    ColidingFixture = null;
                }
            }
            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            spriteBatch.Draw(drawTexture, position + _ScreenOffset, null, Color.White, rotation, sprite.Origin, 1, SpriteEffects.None, 0);
            base.Draw(gameTime);
        }
        #endregion

        #region Default Collision Event Handlers
        /// <summary>
        /// Add this method to defaultFixture.onCollision event handler
        /// i.e. defaultFixture.onCollision += HandleDefaultCollision;
        /// </summary>
        public virtual bool HandleDefaultCollision(Fixture ThisFixture, Fixture OtherFixture, Contact Contact)
        {
            timeSinceLastCollision = 0;
            if (OtherFixture.UserData != null &&
     OtherFixture.UserData.GetType().IsSubclassOf(typeof(Unit))&& !ThisFixture.IsSensor)
            {
                ColidingFixture = OtherFixture;
                ColidedWithThisGP = true;
            }
            return true;
        }
        /// <summary>
        /// Add this method to defaultFixture.onSeperation event handler
        /// i.e. defaultFixture.onSeperation += HandleDefauletAfterCollision;
        /// </summary>
        public virtual void HandleDefaultAfterCollision(Fixture ThisFixture, Fixture OtherFixture)
        {
        }
        #endregion

        #region UnitConversions
        public float ConvertToSim(float displayUnits)
        {
            return ConvertUnits.ToSimUnits(displayUnits);
        }
        public float ConvertToScreen(float simUnits)
        {
            return ConvertUnits.ToDisplayUnits(simUnits);
        }

        public float ConvertToSim(int displayUnits)
        {
            return ConvertUnits.ToSimUnits(displayUnits);
        }
        public float ConvertToScreen(int simUnits)
        {
            return ConvertUnits.ToDisplayUnits(simUnits);
        }

        public Vector2 ConvertToSim(Vector2 displayUnits)
        {
            return ConvertUnits.ToSimUnits(displayUnits);
        }
        public Vector2 ConvertToScreen(Vector2 simUnits)
        {
            return ConvertUnits.ToDisplayUnits(simUnits);
        }

        public void ConvertToSim(ref Vector2 simUnits, out Vector2 displayUnits)
        {
            ConvertUnits.ToSimUnits(ref simUnits, out displayUnits);
        }
        public void ConvertToScreen(ref Vector2 simUnits, out Vector2 displayUnits)
        {
            ConvertUnits.ToDisplayUnits(ref simUnits, out displayUnits);
        }

        public Vector3 ConvertToSim(Vector3 displayUnits)
        {
            return ConvertUnits.ToSimUnits(displayUnits);
        }
        public Vector3 ConvertToScreen(Vector3 simUnits)
        {
            return ConvertUnits.ToDisplayUnits(simUnits);
        }

        public Vector2 ConvertToSim(float x, float y)
        {
            return ConvertUnits.ToSimUnits(x, y);
        }
        public Vector2 ConvertToScreen(float x, float y)
        {
            return ConvertUnits.ToDisplayUnits(x, y);
        }

        public void ConvertToSim(float x, float y, out Vector2 displayUnits)
        {
            ConvertUnits.ToSimUnits(x, y, out displayUnits);
        }
        public void ConvertToScreen(float x, float y, out Vector2 displayUnits)
        {
            ConvertUnits.ToDisplayUnits(x, y, out displayUnits);
        }
        #endregion


        public void ChangeMaterial(Material Material)
        {
            material = Material;
            friction = material.Friction;
            restitution = material.Restitution;
            mass = Material.mass;
            sprite = new Sprite(assetCreator.TextureFromShape(defaultShape, material, Color.White, 0.8f));
        }



    }
}
